#include "zookeeperutil.h"
#include "mprpcapplication.h"

// 全局的watcher观察器（zkserver给zkclient的通知）
void globalWathcer(zhandle_t* zh, int type, int state, const char* path, void* watcherCtx)
{
    if(type == ZOO_SESSION_EVENT) // 回调的消息类型是会话相关的消息类型
    {
        if(state == ZOO_CONNECTED_STATE) // zkclient和zkserver连接成功
        {
            sem_t* sem = (sem_t*)zoo_get_context(zh);
            sem_post(sem);
        }
    }
}

ZkClient::ZkClient()
    : m_zhandle(nullptr)
{}   

ZkClient::~ZkClient()
{
    if(m_zhandle != nullptr)
    {
        zookeeper_close(m_zhandle); // 关闭句柄，释放资源
    }
}

// 连接zkserver
void ZkClient::Start()
{
    std::string host = MprpcApplication::GetInstance().GetConfig().Load("zookeeperip");
    std::string port = MprpcApplication::GetInstance().GetConfig().Load("zookeeperport");
    std::string connStr = host + ":" + port;

    /**
     * zookeeper_mt：多线程版本
     * zookeeper的API客户端程序提供了三个线程
     * API调用线程
     * 网络I/O线程 pthread_create poll
     * watcher回调线程
    */
    // 创建句柄成功，并不意味着连接zkserver成功
    m_zhandle = zookeeper_init(connStr.c_str(), globalWathcer, 30000, nullptr, nullptr, 0);
    if(m_zhandle == nullptr)
    {
        std::cout << "zookeeper_init error!" << std::endl;
    }

    sem_t sem;
    sem_init(&sem, 0, 0);
    zoo_set_context(m_zhandle, &sem); // 设置句柄的上下文

    sem_wait(&sem); // 阻塞等待连接的响应
    std::cout << "zookeeper_init success!" << std::endl;
}

void ZkClient::Create(const char* path, const char* data, int datalen, int state)
{
    char pathBuf[128];
    int bufLen = sizeof(pathBuf);
    int flag = zoo_exists(m_zhandle, path, 0, nullptr);
    // 先判断path表示的znode节点是否存在，如果存在就不再重复创建了
    if(flag == ZNONODE) // 表示path的znode节点不存在
    {
        // 创建指定path的znode节点
        flag = zoo_create(m_zhandle, path, data, datalen,
            &ZOO_OPEN_ACL_UNSAFE, state, pathBuf, bufLen);
        if(flag == ZOK)
        {
            std::cout << "znode create success! path: " << path << std::endl;
        }
        else
        {
            std::cout << "flag: " << flag << std::endl;
            std::cout << "znode create error! path: " << path << std::endl;
            exit(EXIT_FAILURE);  
        }
    }
}

// 根据指定的path获取znode节点的值
std::string ZkClient::GetData(const char* path)
{
    char buf[64];
    int bufLen = sizeof(buf);
    int flag = zoo_get(m_zhandle, path, 0, buf, &bufLen, nullptr);
    if(flag != ZOK)
    {
        std::cout << "get znode error! path: " << path << std::endl;
        return "";
    }
    else
    {
        return buf;
    }
}